#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>

#define NUM 1024
#define SIZE 32
#define SEP " "

//保存完成的命令行字符串
char cmd_line[NUM];
//保存打散之后的命令行字符串
char *g_argv[SIZE];

// shell 运行原理 ：通过让子进程执行进程命令，父进程等待&&解析命令
int main()
{
  //0.命令行解释器，一定是一个常驻内存的进程，不退出
  while(1)
  {
    //1.打印出提示信息 [LX@localhost minishell]#
    printf("[LX@localhost myshell]#");
    
    //由于前面的printf没有\n刷新缓冲区
    //，而又进入下一次循环了，来不及打印，需要用fflush强制将缓冲区的数据刷出来到屏幕上
    fflush(stdout);
    memset(cmd_line,'\0',sizeof cmd_line);

    //2.获取用户的键盘输入[输入的是各种指令和选项]: "ls -a -l -i"
    if(fgets(cmd_line,sizeof cmd_line, stdin)==NULL)
    {
      continue;
    }


    //"ls -a -l -i\n\0"
    //将输入完成指令和选项之后敲的确认键换车给清掉
    //，因为他也会被当成选项和指令输入cmd_line数组中
    //printf("echo: %s\n",cmd_line);
    cmd_line[strlen(cmd_line)-1]='\0';


    //3.命令行字符串解析: "ls -a -l -i"->"ls" "-a" "-i"
    g_argv[0]=strtok(cmd_line,SEP);//第一次调用，要传入原始字符串
    int index=1;
    if(strcmp(g_argv[0],"ls")==0)
    {
      g_argv[index++] = "--color=auto";
    }
    if(strcmp(g_argv[0],"ll")==0)
    {
      g_argv[0]= "ls";
      g_argv[index++]= "-l";
      g_argv[index++]= "--color=auto";
    }
    
    while(g_argv[index++]=strtok(NULL,SEP));//第二次，如果还要解析原始字符串，传入NULL,strtok是分割字符
    
    //for debug
    //for(inedx=0;g_argv[index];index++)
    //printf("g_argv[%d]: %s\n",index,g_argv[index]);
    
    //4.TODO,内置命令，让父进程(shell)自己执行命令，我们叫做内置命令，内建命令
    //内建命令本质其实就是shell中的一个函数调用
    if(strcmp(g_argv[0],"cd")==0)//not child execute, father execute
    {
      if(g_argv[1]!=NULL)
        chdir(g_argv[1]);//cd path,cd ..//chdir是将路径进行更改到g_argv[1]指定的路径中
    }


    //5.fork()
    pid_t id =fork();

    //child
    if(id==0)
    {
      printf("下面功能让子进程进行的\n");
      //cd cmd, current child path
      execvp(g_argv[0],g_argv);//ls -a -l -i
      exit(1);
    }
   
    //father
    int status=0;
    pid_t ret =waitpid(id,&status,0);
    if(ret>0)printf("exit code: %d",WEXITSTATUS(status));
  }
  return 0;
}
